#include "GstVideoReceiver.h"


Q_LOGGING_CATEGORY(VideoReceiverLog, "VideoReceiverLog")

GstVideoReceiver::GstVideoReceiver(QObject *parent)
    : IVideoReceiver(parent)
{
    _worker.start();
    init();
}

GstVideoReceiver::~GstVideoReceiver()
{
    _worker.shutdown();
    _worker.wait();
}

// IVideoReceiver interface

void GstVideoReceiver::init()
{
    _pipeLine = gst_pipeline_new("main_pipeline");
    if (!_pipeLine) {
        qCCritical(VideoReceiverLog) << "Failed to create pipeline";
        return;
    }
    
    // 添加总线监听
    GstBus *bus = gst_pipeline_get_bus(GST_PIPELINE(_pipeLine));
    gst_bus_add_watch(bus, (GstBusFunc)_onBusMessage, this);
    gst_object_unref(bus);
}

void GstVideoReceiver::start(const QString &uri, unsigned int timeout, int buffer, int cacheTime)
{
    if (uri.isEmpty()) {
        qCDebug(VideoReceiverLog) << "Failed because URI is not specified";
        return;
    }

    if (!_checkUrlValid(uri)) {
        qCDebug(VideoReceiverLog) << "Failed because URI is not support!";
        return;
    }

    _sourceUri = uri;
    _timeout = timeout;
    _buffer = buffer;
    _cacheTime = cacheTime;

    qCDebug(VideoReceiverLog) << "VIDEO START: " << uri;

    _startVideo();
}

void GstVideoReceiver::stop()
{
}

void GstVideoReceiver::restart(unsigned long waitTime)
{
}

void GstVideoReceiver::startDecoding(void *sink)
{
    if (sink == NULL) {
        qCDebug(VideoReceiverLog) << "sink invalid, failed";
        return;
    }
    if (_source == NULL) {
        qCDebug(VideoReceiverLog) << "source invalid, failed";
        return;
    }
    _videoSink = (GstElement*)sink;
    gst_bin_add_many(GST_BIN(_pipeLine), _source, _videoSink, NULL);
    
    // 修正连接判断逻辑
    if (!gst_element_link_many(_source, _videoSink, NULL)) {
        qCDebug(VideoReceiverLog) << "element link failed";
        gst_object_unref(_pipeLine);
        return;
    }
    
    // 设置管道状态
    GstStateChangeReturn ret = gst_element_set_state(_pipeLine, GST_STATE_PLAYING);
    if (ret == GST_STATE_CHANGE_FAILURE) {
        qCDebug(VideoReceiverLog) << "Unable to set the pipeline to playing state";
        gst_object_unref(_pipeLine);
        return;
    }
}

void GstVideoReceiver::stopDecoding()
{
    if (_pipeLine) {
        // 停止管道
        gst_element_set_state(_pipeLine, GST_STATE_NULL);
        
        // 移除元素
        if (_source) {
            gst_bin_remove(GST_BIN(_pipeLine), _source);
            _source = NULL;
        }
        
        if (_videoSink) {
            gst_bin_remove(GST_BIN(_pipeLine), _videoSink);
            _videoSink = NULL;
        }
    }
}

void GstVideoReceiver::_startVideo()
{
    if (_needDispatch()) {
        _worker.dispatch([this] {
            this->_startVideo();
        });
        return;
    }

    GstElement *sourceBin = _makeSource(_sourceUri);
    if (sourceBin == NULL) {
        qCDebug(VideoReceiverLog) << "play source bin create failed!";
    }
}

void GstVideoReceiver::_stopVideo()
{

}

void GstVideoReceiver::_watchDog()
{

}

void GstVideoReceiver::_handleEos()
{

}

GstElement *GstVideoReceiver::_makeSource(const QString &uri)
{
    // Debug
    _source = gst_element_factory_make("videotestsrc", NULL);
    return _source;

    // TODO
    qCWarning(VideoReceiverLog) << "Creating source for URI:" << uri;

    return NULL;
}

GstElement *GstVideoReceiver::_makeDecoder(GstCaps *caps, GstElement *videoSink)
{
    return NULL;
}

bool GstVideoReceiver::_initDecoder()
{
    return true;
}

void GstVideoReceiver::_releaseDecoder()
{

}

void GstVideoReceiver::_onNewSourcePad(GstPad *pad)
{

}

void GstVideoReceiver::_onNewDecoderPad(GstPad *pad)
{

}

bool GstVideoReceiver::_addDecoder(GstElement *src)
{
    return true;
}

bool GstVideoReceiver::_addVideoSink(GstPad *pad)
{
    return true;
}

void GstVideoReceiver::_noteTeeFrame()
{

}

void GstVideoReceiver::_noteVideoSinkFrame()
{

}

void GstVideoReceiver::_noteEndOfStream()
{

}

bool GstVideoReceiver::_needDispatch()
{
    return QThread::currentThread() != &_worker;
}

void GstVideoReceiver::_dispatchSignal(std::function<void ()> emitter)
{

}

bool GstVideoReceiver::_checkUrlValid(const QString &url)
{
    return url.contains("rtsp://", Qt::CaseInsensitive);
}

// GSteamer Callback Function

gboolean GstVideoReceiver::_onBusMessage(GstBus *bus, GstMessage *message, gpointer user_data)
{
    return TRUE;
}

void GstVideoReceiver::_onNewPad(GstElement *element, GstPad *pad, gpointer data)
{

}

void GstVideoReceiver::_onSourcePad(GstElement *src, GstPad *pad, gpointer data)
{

}

void GstVideoReceiver::_wrapWithGhostPad(GstElement *element, GstPad *pad, gpointer data)
{

}

void GstVideoReceiver::_linkPad(GstElement *element, GstPad *pad, gpointer data)
{

}

gboolean GstVideoReceiver::_padProbe(GstElement *element, GstPad *pad, gpointer user_data)
{
    return TRUE;
}

gboolean GstVideoReceiver::_filterParserCaps(GstElement *bin, GstPad *pad, GstElement *element, GstQuery *query, gpointer data)
{
    return TRUE;
}

GstPadProbeReturn GstVideoReceiver::_videoSinkProbe(GstPad *pad, GstPadProbeInfo *info, gpointer user_data)
{
    return GST_PAD_PROBE_OK;
}

GstPadProbeReturn GstVideoReceiver::_eosProbe(GstPad *pad, GstPadProbeInfo *info, gpointer user_data)
{
    return GST_PAD_PROBE_OK;
}

GstPadProbeReturn GstVideoReceiver::_keyframeWatch(GstPad *pad, GstPadProbeInfo *info, gpointer user_data)
{
    return GST_PAD_PROBE_OK;
}
